2aff8b5c |
1 | <?xml version="1.0" encoding="utf-8" ?> |
2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" |
3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
4 | <html xmlns="http://www.w3.org/1999/xhtml"> |
5 | <head> |
6 | <title>Metaobject Protocols</title> |
7 | <meta name="generator" content="muse.el" /> |
8 | <meta http-equiv="Content-Type" |
9 | content="text/html; charset=utf-8" /> |
a7e21d41 |
10 | <link href="http://feeds.unknownlamer.org/rss/site-updates" |
11 | rel="alternate" type="application/rss+xml" title="Updates Feed" /> |
12 | |
2aff8b5c |
13 | <link rel="stylesheet" href="default.css" media="screen" /> |
14 | </head> |
15 | <body> |
16 | <h1>Metaobject Protocols</h1> |
17 | <div class="contents"> |
18 | <dl> |
19 | <dt> |
20 | <a href="#sec1">Background</a> |
21 | </dt> |
22 | <dd> |
23 | <dl> |
24 | <dt> |
25 | <a href="#sec2">Object Protocols</a> |
26 | </dt> |
27 | <dt> |
28 | <a href="#sec3">CLOS Way of OO</a> |
29 | </dt> |
30 | <dd> |
31 | <dl> |
32 | <dt> |
a7e21d41 |
33 | <a href="#sec4">Classes for Scratch Data and Types</a> |
2aff8b5c |
34 | </dt> |
35 | <dt> |
a7e21d41 |
36 | <a href="#sec5">Generics with Methods that Implement Protocols</a> |
2aff8b5c |
37 | </dt> |
38 | </dl> |
39 | </dd> |
40 | </dl> |
41 | </dd> |
42 | <dt> |
43 | <a href="#sec6">Limitations of Default Language Behavior</a> |
44 | </dt> |
45 | <dd> |
46 | <dl> |
47 | <dt> |
48 | <a href="#sec7">Slot Storage</a> |
49 | </dt> |
50 | <dt> |
51 | <a href="#sec8">Design Patterns</a> |
52 | </dt> |
53 | </dl> |
54 | </dd> |
55 | <dt> |
56 | <a href="#sec9">Metasoftware</a> |
57 | </dt> |
58 | <dd> |
59 | <dl> |
60 | <dt> |
61 | <a href="#sec10">Runtime Generated Classes</a> |
62 | </dt> |
63 | <dt> |
64 | <a href="#sec11">Object Inspection</a> |
65 | </dt> |
66 | </dl> |
67 | </dd> |
68 | <dt> |
69 | <a href="#sec12">Metaobject Protocols</a> |
70 | </dt> |
71 | <dd> |
72 | <dl> |
73 | <dt> |
74 | <a href="#sec13">Limited/Generalized Internals of the Implementation</a> |
75 | </dt> |
76 | <dt> |
77 | <a href="#sec14">Classes of MOPs</a> |
78 | </dt> |
79 | <dd> |
80 | <dl> |
81 | <dt> |
82 | <a href="#sec15">Reflective</a> |
83 | </dt> |
84 | <dt> |
85 | <a href="#sec16">Intercessory</a> |
86 | </dt> |
87 | </dl> |
88 | </dd> |
89 | <dt> |
90 | <a href="#sec17">Violation of Encapsulation?</a> |
91 | </dt> |
92 | </dl> |
93 | </dd> |
94 | <dt> |
95 | <a href="#sec18">MOP Design Principles</a> |
96 | </dt> |
97 | <dd> |
98 | <dl> |
99 | <dt> |
100 | <a href="#sec19">Layered Protocol</a> |
101 | </dt> |
102 | <dd> |
103 | <dl> |
104 | <dt> |
a7e21d41 |
105 | <a href="#sec20">Top Level <strong>Must</strong> Call Lower Level Methods</a> |
2aff8b5c |
106 | </dt> |
107 | <dt> |
a7e21d41 |
108 | <a href="#sec21">Lower Level Methods are Easier to Customize</a> |
2aff8b5c |
109 | </dt> |
110 | </dl> |
111 | </dd> |
112 | <dt> |
113 | <a href="#sec22">Functional Where Possible</a> |
114 | </dt> |
115 | <dd> |
116 | <dl> |
117 | <dt> |
118 | <a href="#sec23">Memoization</a> |
119 | </dt> |
120 | <dt> |
a7e21d41 |
121 | <a href="#sec24">Constant Shared Return Values</a> |
2aff8b5c |
122 | </dt> |
123 | </dl> |
124 | </dd> |
125 | <dt> |
126 | <a href="#sec25">Procedural Only Where Neccesary</a> |
127 | </dt> |
2aff8b5c |
128 | <dt> |
a7e21d41 |
129 | <a href="#sec26">Real World</a> |
2aff8b5c |
130 | </dt> |
131 | <dd> |
132 | <dl> |
133 | <dt> |
a7e21d41 |
134 | <a href="#sec27">UCW and Arnesi</a> |
2aff8b5c |
135 | </dt> |
136 | <dt> |
a7e21d41 |
137 | <a href="#sec28">CLSQL</a> |
2aff8b5c |
138 | </dt> |
139 | <dt> |
a7e21d41 |
140 | <a href="#sec29">Elephant</a> |
2aff8b5c |
141 | </dt> |
142 | </dl> |
143 | </dd> |
144 | </dl> |
145 | </dd> |
146 | <dt> |
a7e21d41 |
147 | <a href="#sec30">Sources &amp; Further Reading</a> |
2aff8b5c |
148 | </dt> |
149 | <dd> |
150 | <dl> |
151 | <dt> |
a7e21d41 |
152 | <a href="#sec31">Sources</a> |
2aff8b5c |
153 | </dt> |
154 | <dd> |
155 | <dl> |
156 | <dt> |
a7e21d41 |
157 | <a href="#sec32">The Art of the Metaobject Protocol</a> |
2aff8b5c |
158 | </dt> |
159 | <dt> |
a7e21d41 |
160 | <a href="#sec33">CLOS MOP Specification</a> |
2aff8b5c |
161 | </dt> |
162 | <dt> |
a7e21d41 |
163 | <a href="#sec34">Metaobject Protocols: Why We Want Them and What Else They Can Do</a> |
2aff8b5c |
164 | </dt> |
165 | <dt> |
a7e21d41 |
166 | <a href="#sec35">Why Are Black Boxes so Hard to Reuse?</a> |
2aff8b5c |
167 | </dt> |
168 | </dl> |
169 | </dd> |
170 | <dt> |
a7e21d41 |
171 | <a href="#sec36">Further Reading</a> |
2aff8b5c |
172 | </dt> |
173 | <dd> |
174 | <dl> |
175 | <dt> |
a7e21d41 |
176 | <a href="#sec37">A Metaobject Protocol for C++</a> |
2aff8b5c |
177 | </dt> |
178 | <dt> |
a7e21d41 |
179 | <a href="#sec38">Open Implementations and Metaobject Protocols</a> |
2aff8b5c |
180 | </dt> |
181 | </dl> |
182 | </dd> |
183 | </dl> |
184 | </dd> |
185 | </dl> |
186 | </div> |
187 | |
188 | |
189 | <!-- Page published by Emacs Muse begins here --><p>In Fall of 2006 I did a small project on Metaobject Protocols for my |
190 | CS 331 class. Here lie my notes which may perhaps be useful to |
191 | others. I hope to expand them into something more useful over time.</p> |
192 | |
193 | <h2><a name="sec1" id="sec1"></a> |
194 | Background</h2> |
195 | |
196 | <h3><a name="sec2" id="sec2"></a> |
197 | Object Protocols</h3> |
198 | |
199 | <p class="first">An object protocol is a set of methods and specification of the |
200 | interactions between the methods which provide some generic behavior |
201 | (e.g. of a sequence) that are then implemented by classes which |
202 | conform to the protocol (e.g. a vector or list). In most object |
203 | systems a class contains both the methods which implement a protocol |
204 | and the data used by the implementation. The intent is to emulate |
205 | state machines which pass messages between each other.</p> |
206 | |
207 | |
208 | <h3><a name="sec3" id="sec3"></a> |
209 | CLOS Way of OO</h3> |
210 | |
211 | <p class="first">The Common Lisp Object System (CLOS) is different. It separates |
212 | the data and method concepts into classes and generics. A class |
213 | contains data fields only, and a generic has methods specialized for |
214 | certain types attached to it. This seems a bit weird at first, but is |
215 | significantly more powerful as it encourages complete encapsulation |
216 | through its use of classes primarily for method specialization rather |
217 | than for state storage.</p> |
218 | |
2aff8b5c |
219 | <h4><a name="sec4" id="sec4"></a> |
a7e21d41 |
220 | Classes for Scratch Data and Types</h4> |
2aff8b5c |
221 | |
222 | <p class="first">In CLOS classes store data in slots (which are the same as data |
223 | members). Encapsulation is not provided; any bit of code can use |
224 | <code>slot-value</code> to access or set the value of a slot. This may seem odd at |
225 | first, but encapsulation is of questionable importance as the slots |
226 | are meant only to be used by the protocol defined around the class.</p> |
227 | |
a7e21d41 |
228 | <p>Classes are defined with <code>defclass</code></p> |
2aff8b5c |
229 | |
230 | <pre class="src"> |
a7e21d41 |
231 | (<span style="color: #b9d3ee;">defclass</span> <span style="color: #98fb98;">name</span> (superclasses ...) |
2aff8b5c |
232 | ((slot-name <span style="color: #b0c4de;">:accessor</span> slot-accessor ...) |
233 | ...) |
234 | (class-options ...)) |
235 | |
a7e21d41 |
236 | (<span style="color: #b9d3ee;">defclass</span> <span style="color: #98fb98;">example</span> () |
2aff8b5c |
237 | ((foo <span style="color: #b0c4de;">:accessor</span> foo-of <span style="color: #b0c4de;">:initform</span> 5))) |
238 | |
a7e21d41 |
239 | (<span style="color: #b9d3ee;">defclass</span> <span style="color: #98fb98;">example-child</span> (example) |
2aff8b5c |
240 | ((bar <span style="color: #b0c4de;">:accessor</span> bar-of <span style="color: #b0c4de;">:initform</span> (list 1 2 3)))) |
241 | </pre> |
242 | |
a7e21d41 |
243 | <p>Slot defintions have several options; the above example shows only the |
2aff8b5c |
244 | <code>:accessor</code> and <code>:initform</code> options which are the most commonly |
245 | used. <code>:accessor</code> generates an accessor for the slot (e.g. if you have |
a7e21d41 |
246 | an instance of <code>example</code> you can <code>(setf (foo-of some-example-instance) |
247 | 'some-value)</code> to set and <code>(foo-of some-example-instance)</code> to access the |
2aff8b5c |
248 | value). <code>:initform</code> provides a default initial value for the slot as a |
a7e21d41 |
249 | symbolic expression to be evaluated when an instance is created in the |
250 | lexical environment of the class definition.</p> |
2aff8b5c |
251 | |
252 | |
253 | <h4><a name="sec5" id="sec5"></a> |
a7e21d41 |
254 | Generics with Methods that Implement Protocols</h4> |
2aff8b5c |
255 | |
256 | <p class="first">Generics are like normal functions in Lisp, but they only provide a |
257 | lambda list (parameter list). Methods are added to the generic which |
a7e21d41 |
258 | specialize on the types of their parameters and provide an |
259 | implementation. This allows writing rich layered protocols which can |
260 | enable selective modification of individual facets with minimal code.</p> |
2aff8b5c |
261 | |
262 | <pre class="src"> |
a7e21d41 |
263 | (<span style="color: #b9d3ee;">defgeneric</span> <span style="color: #87cefa;">generic</span> (parameters ...) |
2aff8b5c |
264 | (options) ...) |
265 | |
a7e21d41 |
266 | (<span style="color: #b9d3ee;">defmethod</span> <span style="color: #87cefa;">generic-name</span> ((parameter type) parameter ...) |
2aff8b5c |
267 | <span style="color: #b3b3b3;">"documentation string"</span> |
268 | body) |
269 | |
a7e21d41 |
270 | (<span style="color: #b9d3ee;">defgeneric</span> <span style="color: #87cefa;">foo</span> (bar baz quux) |
2aff8b5c |
271 | (<span style="color: #b0c4de;">:documentation</span> <span style="color: #b3b3b3;">"Process the baz with the quux capacitor to make the |
272 | foo widget fly into the sky at warp speed"</span>)) |
273 | |
a7e21d41 |
274 | (<span style="color: #b9d3ee;">defmethod</span> <span style="color: #87cefa;">foo</span> ((bar example) baz (quux capacitor)) |
2aff8b5c |
275 | (launch bar (process-with quux baz))) |
276 | </pre> |
277 | |
278 | <p>A method lambda list differs from a normal lambda list only in that it |
279 | can specify the type of the parameter using the notation <code>(name type)</code>. |
280 | Note also that methods can specialize on the types of every |
281 | argument and not just the first one. This is quite powerful for |
282 | reasons outside of the scope of this presentation.</p> |
283 | |
284 | |
285 | |
286 | |
287 | <h2><a name="sec6" id="sec6"></a> |
288 | Limitations of Default Language Behavior</h2> |
289 | |
290 | <p class="first">The behavior of a language is a compromise between many competing |
a7e21d41 |
291 | issues that attempts to be as generally useful as possible so that |
292 | <em>most</em> applications will have no issue with the default behavior. There |
293 | are, however, certain applications that could be cleanly written with |
294 | minor modifications to the behavior of the language, but would be |
295 | impossible or quite difficult to write otherwise.</p> |
2aff8b5c |
296 | |
297 | <h3><a name="sec7" id="sec7"></a> |
298 | Slot Storage</h3> |
299 | |
300 | <p class="first">Most languages choose to preallocate storage for all of the slots of |
a7e21d41 |
301 | an instance. Now imagine a contact database that stores information |
302 | about people in slots of a class. There may be dozens of slots, but |
303 | often many of them will be left blank. If slot storage is preallocated |
304 | much memory will be wasted and the database may not be able to fit |
305 | into the memory of the hardware it must run on (perhaps for financial |
306 | reasons, huge datasets, etc.).</p> |
2aff8b5c |
307 | |
308 | <p>To save memory the author of the contact database must implement his |
309 | own system to store properties and allocate them lazily. This |
310 | represents a fair bit of effort, and would implement a system that |
a7e21d41 |
311 | differed from the existing slot system of classes only regarding slot |
312 | storage.</p> |
2aff8b5c |
313 | |
a7e21d41 |
314 | <p>It would be useful if there were a way to customize slot allocation in |
315 | instances. The customizations would be minor and require overriding |
2aff8b5c |
316 | only the initial allocation behavior and the behavior of the first |
317 | assignment to the slot. It is a a trivial problem in a language that |
a7e21d41 |
318 | allows customization of these behaviors.</p> |
2aff8b5c |
319 | |
320 | |
321 | <h3><a name="sec8" id="sec8"></a> |
322 | Design Patterns</h3> |
323 | |
324 | <p class="first">Design Patterns are generalized versions of common patterns found in |
325 | programs. Many of them are merely methods to get around deficiencies |
326 | in the language, and can be quite messy to implement in some |
a7e21d41 |
327 | languages. Ideally a pattern would be subsumed by the language, but |
328 | real world contraints require language standards to remain fairly |
329 | static.</p> |
2aff8b5c |
330 | |
331 | |
332 | |
333 | <h2><a name="sec9" id="sec9"></a> |
334 | Metasoftware</h2> |
335 | |
336 | <p class="first">Some types of programs could be written easily if the language were |
a7e21d41 |
337 | customizable but are nearly impossible to write when it is not.</p> |
2aff8b5c |
338 | |
339 | <h3><a name="sec10" id="sec10"></a> |
340 | Runtime Generated Classes</h3> |
341 | |
342 | <p class="first">Say you wanted to write a video game where players could create their |
343 | own objects, attach behaviors to the objects, and perhaps mix |
344 | different objects together to create new ones. When you abstract the |
345 | problem this looks just like an object system! Wouldn't it be nice if |
a7e21d41 |
346 | your program could create new classes and methods on the fly portably?</p> |
2aff8b5c |
347 | |
348 | |
349 | <h3><a name="sec11" id="sec11"></a> |
350 | Object Inspection</h3> |
351 | |
a7e21d41 |
352 | <p class="first">Imagine you were developing a complicated program with many different |
353 | objects that interacted in fairly complex ways. A tool to inspect the |
354 | structure of objects while debugging would be quite useful, but in a |
355 | traditional language would be impossible to implement portably. This |
356 | could force you to purchase a certain compiler implementation which |
357 | provided an inspector, and even then would likely not be customizable.</p> |
2aff8b5c |
358 | |
359 | <p>This problem can be generalized to apply to most debugging tools; it |
360 | would be useful to write such tools portably because users of the |
361 | <em>language</em> and not the <em>compiler</em> need to debug software. Sharing |
362 | infrastructure would result in better tools (more developers), and |
a7e21d41 |
363 | save the man-years of wasted effort that comes with having to rewrite |
364 | unportable tools from scratch multiple times.</p> |
2aff8b5c |
365 | |
366 | |
367 | |
368 | <h2><a name="sec12" id="sec12"></a> |
369 | Metaobject Protocols</h2> |
370 | |
371 | <h3><a name="sec13" id="sec13"></a> |
372 | Limited/Generalized Internals of the Implementation</h3> |
373 | |
a7e21d41 |
374 | <p class="first">A Metaobject Protocol (MOP) is a generalized and limited subset of the |
375 | underlying language implementation. It is limited to allow multiple |
376 | implementation strategies; this, along with careful design, is |
377 | essential because programming language research is ever advancing and |
378 | new techniques for creating more reliable and faster implementations |
379 | are still being discovered.</p> |
2aff8b5c |
380 | |
381 | <p>This subset of the implementation is exported as a set of methods on |
a7e21d41 |
382 | metaobjects. Thus the language is implemented in itself. The system |
383 | can then be customized using the extension and overriding features of |
384 | the language itself.</p> |
2aff8b5c |
385 | |
386 | |
387 | <h3><a name="sec14" id="sec14"></a> |
388 | Classes of MOPs</h3> |
389 | |
390 | <h4><a name="sec15" id="sec15"></a> |
391 | Reflective</h4> |
392 | |
a7e21d41 |
393 | <p class="first">A reflective MOP provides an interface to information <em>about</em> the |
394 | running system. It exposes class relationships, the methods attached |
395 | to a generic, etc. A reflective MOP often provides some functionality |
396 | for creating new classes at runtime. Smalltalk was one of the first |
397 | languages to expose a reflective MOP.</p> |
2aff8b5c |
398 | |
399 | <h5>Example: Object Inspector</h5> |
400 | |
2aff8b5c |
401 | <pre class="src"> |
a7e21d41 |
402 | (<span style="color: #b9d3ee;">defgeneric</span> <span style="color: #87cefa;">example-inspect</span> (instance) |
2aff8b5c |
403 | (<span style="color: #b0c4de;">:documentation</span> <span style="color: #b3b3b3;">"Simple object inspector using CLOS MOP"</span>)) |
404 | |
a7e21d41 |
405 | (<span style="color: #b9d3ee;">defmethod</span> <span style="color: #87cefa;">example-inspect</span> ((instance t)) |
2aff8b5c |
406 | (format t <span style="color: #b3b3b3;">"Simple Object~% Value: ~S~%"</span> instance)) |
407 | |
a7e21d41 |
408 | (<span style="color: #b9d3ee;">defmethod</span> <span style="color: #87cefa;">example-inspect</span> ((instance standard-object)) |
409 | (<span style="color: #b9d3ee;">let</span> ((class (class-of instance))) |
2aff8b5c |
410 | (format t <span style="color: #b3b3b3;">"Class: ~S, Superclasses: ~S~%"</span> |
411 | (class-name class) |
412 | (mapcar #'class-name |
413 | (class-precedence-list class))) |
a7e21d41 |
414 | (<span style="color: #b9d3ee;">let</span> ((slot-names (mapcar #'slot-definition-name |
2aff8b5c |
415 | (class-slots class)))) |
416 | (format t <span style="color: #b3b3b3;">"Slots: ~%~{ ~S~%~}"</span> slot-names) |
417 | (inspect-loop slot-names instance #'example-inspect)))) |
418 | |
a7e21d41 |
419 | (<span style="color: #b9d3ee;">defun</span> <span style="color: #87cefa;">inspect-loop</span> (slots instance inspector) |
2aff8b5c |
420 | (format t <span style="color: #b3b3b3;">"Enter slot to inspect or :pop to go up one level: "</span>) |
421 | (finish-output) |
a7e21d41 |
422 | (<span style="color: #b9d3ee;">let*</span> ((slot (read)) |
2aff8b5c |
423 | (found-slot (member slot slots))) |
a7e21d41 |
424 | (<span style="color: #b9d3ee;">cond</span> (found-slot |
2aff8b5c |
425 | (funcall inspector (slot-value instance slot)) |
426 | (funcall inspector instance)) |
427 | ((eq slot <span style="color: #b0c4de;">:pop</span>) t) |
428 | (t |
429 | (format t <span style="color: #b3b3b3;">"~S is invalid. Valid slot names: ~S~%"</span> |
430 | slot |
431 | slots) |
432 | (inspect-loop slots instance inspector))))) |
433 | </pre> |
434 | |
435 | |
a7e21d41 |
436 | <h5>Example: Runtime Generated Classes and Methods</h5> |
437 | |
438 | |
439 | |
440 | <h4><a name="sec16" id="sec16"></a> |
441 | Intercessory</h4> |
442 | |
443 | <p class="first">Intercessory MOPs allow the user to customize language behavior by |
444 | implementing methods which override certain aspects of the language |
445 | behavior. This class of MOPs are what make MOPs especially |
446 | powerful. No longer must a problem be restructured to fit the |
447 | implementation language; the underyling language can be reshaped to |
448 | fit the task at hand, and obfuscation of the intended structure of the |
449 | application can be avoided.</p> |
450 | |
451 | <h5>Example: Lazily Allocated Slots</h5> |
452 | |
453 | |
454 | <h5>Example: Observer Design Pattern</h5> |
2aff8b5c |
455 | |
a7e21d41 |
456 | <p>A simple implementation of the observer pattern is under 100 lines, |
2aff8b5c |
457 | and the user level code requires only a single line of code to make |
458 | any existing class observable.</p> |
459 | |
460 | <p>In a language lacking a MOP, implementing the observer pattern |
461 | requires modifying every accessor of a class to explicitly invoke any |
462 | observers, and neccesitates the addition of a mixin class to the class |
463 | heirarchy. The fact that an object can be observed is a meta property |
464 | of the class, and forcing it to be implemented at the application |
465 | level dirties the inheritance heirarchy and adds uneccesary meta |
466 | details to the program.</p> |
467 | |
468 | <pre class="src"> |
469 | <span style="color: #ff7f24;">;;; </span><span style="color: #ff7f24;">This metaclass adds a slot to instances which use it, and so the |
470 | </span><span style="color: #ff7f24;">;;; </span><span style="color: #ff7f24;">system is defined in its own package to avoid name conflicts |
a7e21d41 |
471 | </span>(<span style="color: #b9d3ee;">defpackage</span> <span style="color: #98fb98;">:observer</span> |
2aff8b5c |
472 | (<span style="color: #b0c4de;">:use</span> <span style="color: #b0c4de;">:cl</span> #+sbcl <span style="color: #b0c4de;">:sb-mop</span>) |
473 | (<span style="color: #b0c4de;">:export</span> observable register-observer unregister-observer)) |
474 | |
a7e21d41 |
475 | (<span style="color: #b9d3ee;">in-package</span> <span style="color: #b0c4de;">:observer</span>) |
2aff8b5c |
476 | |
477 | <span style="color: #ff7f24;">;;; </span><span style="color: #ff7f24;">Metaclass |
a7e21d41 |
478 | </span>(<span style="color: #b9d3ee;">defclass</span> <span style="color: #98fb98;">observable</span> (standard-class) |
2aff8b5c |
479 | () |
480 | (<span style="color: #b0c4de;">:documentation</span> <span style="color: #b3b3b3;">"Metaclass for observable objects"</span>)) |
481 | |
a7e21d41 |
482 | (<span style="color: #b9d3ee;">defmethod</span> <span style="color: #87cefa;">compute-slots</span> ((class observable)) |
2aff8b5c |
483 | <span style="color: #b3b3b3;">"Add a slot for storing observers to observable instances"</span> |
484 | (cons (make-instance 'standard-effective-slot-definition |
485 | <span style="color: #b0c4de;">:name</span> 'observers |
486 | <span style="color: #b0c4de;">:initform</span> '(make-hash-table) |
a7e21d41 |
487 | <span style="color: #b0c4de;">:initfunction</span> #'(<span style="color: #b9d3ee;">lambda</span> () (make-hash-table))) |
2aff8b5c |
488 | (call-next-method))) |
489 | |
a7e21d41 |
490 | (<span style="color: #b9d3ee;">defmethod</span> <span style="color: #87cefa;">validate-superclass</span> ((class observable) |
2aff8b5c |
491 | (super standard-class)) |
492 | t) |
493 | |
a7e21d41 |
494 | (<span style="color: #b9d3ee;">defun</span> <span style="color: #87cefa;">register-observer</span> (instance slot-name key closure) |
2aff8b5c |
495 | (register-observer-with-class (class-of instance) |
496 | instance |
497 | slot-name |
498 | key |
499 | closure)) |
500 | |
a7e21d41 |
501 | (<span style="color: #b9d3ee;">defun</span> <span style="color: #87cefa;">unregister-observer</span> (instance slot-name key) |
2aff8b5c |
502 | (unregister-observer-with-class (class-of instance) |
503 | instance |
504 | slot-name |
505 | key)) |
506 | |
a7e21d41 |
507 | (<span style="color: #b9d3ee;">defun</span> <span style="color: #87cefa;">get-observers</span> (instance slot-name) |
2aff8b5c |
508 | (get-observers-with-class (class-of instance) |
509 | instance |
510 | slot-name)) |
511 | |
a7e21d41 |
512 | (<span style="color: #b9d3ee;">defun</span> <span style="color: #87cefa;">add-observer-table</span> (instance slot-name) |
2aff8b5c |
513 | (setf (gethash slot-name (slot-value instance |
514 | 'observers)) |
515 | (make-hash-table))) |
516 | |
a7e21d41 |
517 | (<span style="color: #b9d3ee;">defgeneric</span> <span style="color: #87cefa;">register-observer-with-class</span> (class instance slot-name key closure)) |
518 | (<span style="color: #b9d3ee;">defgeneric</span> <span style="color: #87cefa;">unregister-observer-with-class</span> (class |
2aff8b5c |
519 | instance |
520 | slot-name |
521 | key)) |
522 | |
a7e21d41 |
523 | (<span style="color: #b9d3ee;">defmethod</span> <span style="color: #87cefa;">register-observer-with-class</span> ((class observable) |
2aff8b5c |
524 | instance |
525 | slot-name |
526 | key |
527 | closure) |
528 | (setf (gethash key |
529 | (or (gethash slot-name |
530 | (slot-value instance 'observers)) |
531 | <span style="color: #ff7f24;">;; </span><span style="color: #ff7f24;">Lazily add observer hash tables |
532 | </span> (add-observer-table instance slot-name))) |
533 | closure)) |
534 | |
a7e21d41 |
535 | (<span style="color: #b9d3ee;">defmethod</span> <span style="color: #87cefa;">unregister-observer-with-class</span> ((class observable) |
2aff8b5c |
536 | instance |
537 | slot-name |
538 | key) |
539 | (remhash key (gethash slot-name |
540 | (slot-value instance 'observers)))) |
541 | |
a7e21d41 |
542 | (<span style="color: #b9d3ee;">defmethod</span> <span style="color: #87cefa;">get-observers-with-class</span> ((class observable) |
2aff8b5c |
543 | instance |
544 | slot-name) |
545 | (gethash slot-name (slot-value instance 'observers))) |
546 | |
a7e21d41 |
547 | (<span style="color: #b9d3ee;">defmethod</span> (<span style="color: #87cefa;">setf slot-value-using-class)</span> <span style="color: #b0c4de;">:before</span> (new-value |
2aff8b5c |
548 | (class observable) |
549 | instance |
550 | slot) |
a7e21d41 |
551 | (<span style="color: #b9d3ee;">let</span> ((slot-name (slot-definition-name slot))) |
552 | (<span style="color: #b9d3ee;">if</span> (not (eq 'observers slot-name)) |
553 | (<span style="color: #b9d3ee;">let</span> ((observers |
2aff8b5c |
554 | (get-observers instance (slot-definition-name slot)))) |
a7e21d41 |
555 | (<span style="color: #b9d3ee;">if</span> observers |
556 | (maphash #'(<span style="color: #b9d3ee;">lambda</span> (key observer) |
2aff8b5c |
557 | (funcall observer |
a7e21d41 |
558 | (<span style="color: #b9d3ee;">if</span> (slot-boundp instance slot-name) |
2aff8b5c |
559 | (slot-value instance slot-name) |
560 | nil) |
561 | new-value)) |
562 | observers)))))) |
563 | </pre> |
564 | |
565 | |
a7e21d41 |
566 | |
567 | |
568 | |
569 | <h3><a name="sec17" id="sec17"></a> |
570 | Violation of Encapsulation?</h3> |
571 | |
572 | <p class="first">A MOP may seem like a violation of encapsulation by revealing some |
573 | implementation details, but in reality a well designed protocol does |
574 | not reveal anything which was not already exposed. Implementation |
575 | decisions affect users, and some of these details do leak through to |
576 | higher levels (e.g. the memory layout of slots). Implicit in the |
577 | protocol specification are these implementation details, and the MOP |
578 | merely makes this limited subset available for customization.</p> |
579 | |
580 | <p>A MOP makes it possible to customize certain implementation decisions |
581 | that do not <strong>radically</strong> alter the behavior of the base language. The |
582 | conceptual vocabulary of the system retains its meaning, and so code |
583 | written in one dialect can interact with code written in another |
584 | without knowing that they speak different ones.</p> |
585 | |
586 | |
587 | |
588 | <h2><a name="sec18" id="sec18"></a> |
589 | MOP Design Principles</h2> |
590 | |
591 | <h3><a name="sec19" id="sec19"></a> |
592 | Layered Protocol</h3> |
593 | |
594 | <p class="first">A layered protocol design is good for both meta and normal object |
595 | protocols, and enables a combinatorial explosion of customizations to |
596 | the protocol.</p> |
597 | |
598 | <h4><a name="sec20" id="sec20"></a> |
599 | Top Level <strong>Must</strong> Call Lower Level Methods</h4> |
600 | |
601 | <p class="first">The top level methods of a layered protocol are required to call |
602 | certain lower level methods to perform some tasks. This both makes it |
603 | easier to customize the top level methods (which perform very broad |
604 | tasks) by providing some pieces of implementation for the programmer, |
605 | and enables more customization by opening up the replacement of lower |
606 | level functions as a way to alter a small detail of the high level |
607 | behavior.</p> |
608 | |
609 | |
610 | <h4><a name="sec21" id="sec21"></a> |
611 | Lower Level Methods are Easier to Customize</h4> |
612 | |
613 | <p class="first">The lower level methods of a MOP are limited in scope and can be |
614 | implemented easily. Often the desired changes to language behavior are |
615 | minor, and having methods that perform simple tasks which are often |
616 | customized reduces the effort required to extend the system.</p> |
617 | |
618 | |
619 | |
620 | <h3><a name="sec22" id="sec22"></a> |
621 | Functional Where Possible</h3> |
622 | |
623 | <p class="first">Functional protocols are preferred for MOPs (and object protocols in |
624 | general). Functional protocols open up several optimizations for the |
625 | implementation without burdening the user of the protocol.</p> |
626 | |
627 | <h4><a name="sec23" id="sec23"></a> |
628 | Memoization</h4> |
629 | |
630 | <p class="first">Memoization is the process of saving the results of a function call |
631 | for future use. This avoids expensive recomputation of values which |
632 | have not changed (recall that a true function will always return the |
633 | same result when given the same arguments).</p> |
634 | |
635 | <p>A functional MOP can be optimized easily by exploiting this property |
636 | to memoize the return values of calls to expensive operations. A MOP |
637 | must be be very fast to avoid making programs unusably slow, and |
638 | memoization is able to give an appreciable speedup in many cases |
639 | without a significant burden on memory usage.</p> |
640 | |
641 | |
642 | <h4><a name="sec24" id="sec24"></a> |
643 | Constant Shared Return Values</h4> |
644 | |
645 | <p class="first">Disallowing modification of values returned by protocol methods allows |
646 | the implementation to return large data structures by reference to |
647 | avoid expensive copying without having to do expensive data integrity |
648 | checks or copying.</p> |
649 | |
650 | |
651 | |
652 | <h3><a name="sec25" id="sec25"></a> |
653 | Procedural Only Where Neccesary</h3> |
654 | |
655 | <p class="first">Some operations like method invocation are inheretly stateful and so |
656 | must use a procedural protocol. There is no benefit to be gained from |
657 | using a functional protocol, and indeed an attempt would result in |
658 | obtuse code that severely restricted the implementian. Do note that |
659 | only a very small part of method invocation is stateful (the actual |
660 | call), and most of it can be implemented functionally (e.g. computing |
661 | the discriminating function).</p> |
662 | |
663 | |
664 | <h3><a name="sec26" id="sec26"></a> |
2aff8b5c |
665 | Real World</h3> |
666 | |
a7e21d41 |
667 | <h4><a name="sec27" id="sec27"></a> |
2aff8b5c |
668 | <a href="http://common-lisp.net/project/ucw/">UCW</a> and <a href="http://common-lisp.net/project/bese/arnesi.html">Arnesi</a></h4> |
669 | |
670 | <p class="first">Arnesi uses the CLOS MOP to implement methods which are transparantly |
671 | rewritten into continuation passing style. This allows their execution |
672 | to be suspended at certain points and resumed later. UCW builds on top |
673 | of this to support a web framework where the statelessness of http is |
674 | hidden from the user; displaying a page suspends the execution of the |
675 | current continuation, and resumes it upon submission. The user level |
676 | code is completely unaware of this.</p> |
677 | |
678 | |
a7e21d41 |
679 | <h4><a name="sec28" id="sec28"></a> |
2aff8b5c |
680 | <a href="http://clsql.b9.com">CLSQL</a></h4> |
681 | |
682 | <p class="first">CLSQL uses the reflective part of the CLOS MOP to map Common Lisp data |
683 | types into SQL types, and the intercessory protocol for slot |
684 | allocation to map slots onto database columns or sql expressions (for |
685 | implementing relational slots).</p> |
686 | |
687 | |
a7e21d41 |
688 | <h4><a name="sec29" id="sec29"></a> |
2aff8b5c |
689 | <a href="http://common-lisp.net/project/elephant/">Elephant</a></h4> |
690 | |
691 | <p class="first">Elephant uses the CLOS MOP to transparantly store any class to disk |
a7e21d41 |
692 | and handle paging between the disk store and memory efficiently |
693 | without user intervention.</p> |
2aff8b5c |
694 | |
695 | |
696 | |
697 | |
a7e21d41 |
698 | <h2><a name="sec30" id="sec30"></a> |
2aff8b5c |
699 | Sources &amp; Further Reading</h2> |
700 | |
a7e21d41 |
701 | <h3><a name="sec31" id="sec31"></a> |
2aff8b5c |
702 | Sources</h3> |
703 | |
a7e21d41 |
704 | <h4><a name="sec32" id="sec32"></a> |
2aff8b5c |
705 | The Art of the Metaobject Protocol</h4> |
706 | |
707 | <h5>Kiczales, Gregor et al. MIT Press 1991</h5> |
708 | |
709 | <p>Highly recommended reading even if you plan to never implement a MOP |
710 | or use the CLOS one. The design principles it recommends are quite |
711 | useful.</p> |
712 | |
713 | |
714 | |
a7e21d41 |
715 | <h4><a name="sec33" id="sec33"></a> |
2aff8b5c |
716 | <a href="http://www.lisp.org/mop/contents.html">CLOS MOP Specification</a></h4> |
717 | |
718 | <p class="first">Specification of the MOP for CLOS defined in <em>The Art of the Metaobject Protocol</em>.</p> |
719 | |
720 | |
a7e21d41 |
721 | <h4><a name="sec34" id="sec34"></a> |
2aff8b5c |
722 | <a href="http://citeseer.ist.psu.edu/399658.html">Metaobject Protocols: Why We Want Them and What Else They Can Do</a></h4> |
723 | |
724 | <p class="first">A short overview of MOP design principles followed by three example |
725 | metaobject protocols for Scheme.</p> |
726 | |
727 | |
a7e21d41 |
728 | <h4><a name="sec35" id="sec35"></a> |
2aff8b5c |
729 | <a href="http://www2.parc.com/csl/groups/sda/projects/oi/towards-talk/transcript.html">Why Are Black Boxes so Hard to Reuse?</a></h4> |
730 | |
731 | <p class="first">Transcription of a talk on the benefits of open implementations of |
732 | software. It first discusses several problems that black box software |
733 | implementations pose, and then presents existing solutions. It shows |
734 | how the existing solutions are insufficient, and then provides |
735 | metaobject protocols as a solution to most of the problems.</p> |
736 | |
737 | |
738 | |
a7e21d41 |
739 | <h3><a name="sec36" id="sec36"></a> |
2aff8b5c |
740 | Further Reading</h3> |
741 | |
a7e21d41 |
742 | <h4><a name="sec37" id="sec37"></a> |
2aff8b5c |
743 | <a href="http://citeseer.ist.psu.edu/chiba95metaobject.html">A Metaobject Protocol for C++</a></h4> |
744 | |
745 | <p class="first">Example of a purely compile time MOP. It implements the functionality |
746 | of a code walker and something similar to the Lisp macro system.</p> |
747 | |
748 | |
a7e21d41 |
749 | <h4><a name="sec38" id="sec38"></a> |
2aff8b5c |
750 | <a href="http://www.parc.com/csl/groups/sda/publications/papers/Kiczales-TUT95/for-web.pdf">Open Implementations and Metaobject Protocols</a></h4> |
751 | |
752 | <p class="first">It is a bit long, but it seems to follow a similar structure to AMOP |
753 | in introducing MOPs and their usefulness. The pages are slides with |
754 | notes, and so the 331 pages might not actually take that long to read.</p> |
755 | |
756 | |
757 | |
758 | |
759 | <!-- Page published by Emacs Muse ends here --> |
760 | |
761 | <p class="cke-buttons"> |
762 | <!-- validating badges, any browser, etc --> |
763 | <a href="http://validator.w3.org/check/referer"><img |
764 | src="http://www.w3.org/Icons/valid-xhtml10" |
765 | alt="Valid XHTML 1.0!" /></a> |
766 | |
767 | <a href="http://www.anybrowser.org/campaign/"><img |
768 | src="img/buttons/w3c_ab.png" alt="[ Viewable With Any Browser |
769 | ]" /></a> |
770 | |
771 | <a href="http://www.debian.org/"><img |
772 | src="img/buttons/debian.png" alt="[ Powered by Debian ]" /></a> |
773 | |
774 | <a href="http://hcoop.net/"> |
775 | <img src="img/buttons/hcoop.png" |
776 | alt="[ Hosted by HCoop]" /> |
777 | </a> |
778 | |
779 | <a href="http://www.fsf.org/register_form?referrer=114"> |
780 | <img src="img/buttons/fsf_member.png" |
781 | alt="[ FSF Associate Member ]" /> |
782 | </a> |
783 | </p> |
784 | |
a7e21d41 |
785 | <p class="cke-footer">* jeffcovey becomes too groggy to read the directions and becomes |
786 | the year's first nasal spray overdose fatality. |
2aff8b5c |
787 | </p> |
788 | <p class="cke-timestamp">Last Modified: |
a7e21d41 |
789 | September 26, 2008</p> |
2aff8b5c |
790 | </body> |
791 | </html> |